package proxy;

import java.io.*;
import java.net.*;
import java.security.*;
import java.util.*;
import javax.net.*;
import javax.net.ssl.*;

public class ClientProxy implements Runnable {
	public static void main(String[] args) throws Exception { new ClientProxy(4440, 4441); }
	private int serverport;
	private int listenport;
	private SSLContext sslContext;
	private SecureRandom secureRandom;
	private SSLSocket serv;
	private Socket client;
	
	public ClientProxy(int listenPort, int connPort) throws Exception {
		serverport = connPort;
		listenport = listenPort;
		new Thread(this).start();
	}
	
	public void run() {	
		System.out.println( "Wait while secure random numbers are initialized...." );
	    secureRandom = new SecureRandom();
	    secureRandom.nextInt();
	    System.out.println( "Done." );
		try {
			setupKeyStoresAndSSLContext();
		} catch (GeneralSecurityException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		SSLSocketFactory sf = sslContext.getSocketFactory();
		
		ServerSocket listen=null;
		try {
			listen = new ServerSocket(listenport);
		} catch (IOException e) {
			e.printStackTrace();
		}
		while (true) {
			try {
				System.out.println("waiting for client...");
				client = listen.accept();
				InputStream cliin = client.getInputStream();
				OutputStream cliout = client.getOutputStream();
				System.out.println("got client");
				
				System.out.println("setting up server connection...");
				serv = (SSLSocket)sf.createSocket("localhost", serverport );
				InputStream servin = serv.getInputStream();
				OutputStream servout = serv.getOutputStream();
				System.out.println("connected to server");
				
				Thread a = new Thread(new Listener(cliin, servout));
				Thread b = new Thread(new Listener(servin, cliout));
				
				a.start();
				b.start();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	
	private void close() {
		try {
			client.close();
			serv.close();
			System.out.println("closed connection");
		} catch (IOException e) { }
	}
	
	private class Listener implements Runnable {
		private InputStream src;
		private OutputStream dest;

		public Listener(InputStream source, OutputStream destination) {
			src = source;
			dest = destination;
		}

		@Override
		public void run() {
			try {
				int i;
				while ((i = src.read()) != -1) {
					dest.write(i);
				}
			} catch (Exception e) {
			}
			close();
		}
	}
	
  private void setupKeyStoresAndSSLContext() throws GeneralSecurityException, IOException {
	  KeyStore serverKeyStore;
	  KeyStore clientKeyStore;
	  String passphrase = "clientpw";
    serverKeyStore = KeyStore.getInstance( "JKS" );
    serverKeyStore.load( new FileInputStream( "server.public" ), 
                        "public".toCharArray() );
    
    clientKeyStore = KeyStore.getInstance( "JKS" );
    clientKeyStore.load( new FileInputStream( "client.private" ),
                       passphrase.toCharArray() );
    
    TrustManagerFactory tmf = TrustManagerFactory.getInstance( "SunX509" );
    tmf.init( serverKeyStore );

    KeyManagerFactory kmf = KeyManagerFactory.getInstance( "SunX509" );
    kmf.init( clientKeyStore, passphrase.toCharArray() );

    sslContext = SSLContext.getInstance( "TLS" );
    sslContext.init( kmf.getKeyManagers(),
                     tmf.getTrustManagers(),
                     secureRandom );
  }

}
